筆記目錄

Skip to content

如何在發佈 ASP.NET Core 網站時自動套用 Shadow Copying 設定

TLDR

  • Shadow Copying 可解決 ASP.NET Core 運行時 DLL 被鎖定導致無法更新的問題。
  • 透過在專案根目錄預置 web.config,發佈時會自動合併設定,無需手動修改伺服器檔案,適合 CI/CD 流程。
  • Shadow Copying 的設定參數會隨 Hosting Bundle 版本變動:7.0+ 使用 enableShadowCopy,6.0 使用 experimentalEnableShadowCopy
  • Shadow Copying 機制並非版本管理工具,其設計為「僅保留當前版本」,每次啟動會嘗試刪除目錄下其他舊資料夾。
  • 升級 .NET 版本後務必手動清理 Shadow Copying 目錄,否則會觸發 500.30 錯誤。
  • 應避免在應用程式目錄中寫入檔案或 Log,以免頻繁觸發 Shadow Copying 導致應用程式重啟。

解決 DLL 鎖定與自動化部署問題

什麼情況下會遇到這個問題:當 ASP.NET Core 應用程式在 IIS 上運行時,相關 DLL 檔案會被鎖定,導致無法直接覆蓋更新,必須先停止應用程式集區。

傳統做法是手動修改伺服器上的 web.config,但這不符合自動化部署原則,且容易因人工疏失導致設定遺失。更好的做法是在 Web 專案根目錄預先建立一個包含 Shadow Copying 設定的 web.config。發佈時,MSBuild 會自動將此檔案作為基礎進行合併,確保部署後的設定正確無誤。

若要同時設定環境變數(如 ASPNETCORE_ENVIRONMENT),可在 .pubxml 中定義 <EnvironmentName>,發佈後的 web.config 將會自動合併環境變數與 Shadow Copying 設定:

xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\WebApi.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess">
        <handlerSettings>
          <handlerSetting name="enableShadowCopy" value="true" />
          <handlerSetting name="shadowCopyDirectory" value="../ShadowCopy/" />
        </handlerSettings>
        <environmentVariables>
          <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Staging" />
        </environmentVariables>
      </aspNetCore>
    </system.webServer>
  </location>
</configuration>

透過此方式,CI/CD 流程僅需執行標準指令即可:

bash
dotnet publish -c Release -p:EnvironmentName=Staging

版本差異注意事項

設定參數名稱取決於伺服器安裝的 Hosting Bundle 版本,而非專案本身的 .NET 版本:

  • Hosting Bundle 7.0.0 以上:使用 enableShadowCopy
  • Hosting Bundle 6.0.0:使用 experimentalEnableShadowCopy

關於 Shadow Copying 的資料夾管理機制

什麼情況下會遇到這個問題:開發者誤以為 shadowCopyDirectory 會自動維護多個版本供回滾使用,或擔心該資料夾會無限膨脹。

根據 AspNetCoreModuleV2 的原始碼分析,Shadow Copying 的設計初衷並非版本管理。當系統選定一個新的流水號資料夾作為執行路徑後,會立即啟動執行緒刪除該目錄下所有其他的資料夾。若發現目錄內有多個資料夾並存,通常是清理執行緒尚未執行完畢,或是舊檔案因鎖定而刪除失敗,而非系統有意保留。

WARNING

  • 升級 .NET 版本後,必須手動刪除 Shadow Copying 資料夾,否則 IIS 會出現 500.30 錯誤,事件檢視器會顯示 directory_iterator 找不到路徑的異常。
  • 使用 Shadow Copying 時,應避免在應用程式目錄中寫入檔案或 Log,否則每次檔案異動都可能觸發 Shadow Copying 機制,導致應用程式不必要的重啟。

異動歷程

  • 2025-03-18 初版文件建立。
  • 2025-04-08 補充升級 .NET 版本造成 Shadow Copying 異常。
  • 2025-06-27 補充使用 Shadow Copying 時需避免在應用程式目錄寫入檔案的注意事項。
  • 2026-01-22 補充 Shadow Copying 資料夾管理機制的常見誤解與源碼說明。